/*!
    \file    dmx512.c
    \brief   DMX512灯带驱动实现文件

    \version 2025-01-20, V1.0.0, DMX512驱动 for GD32C231C
*/
#include "dmx512.h"
#include "systick.h"
#include <string.h>

/* 全局DMX512句柄 */
dmx512_handle_t g_dmx512;

/* 静态函数声明 */
static void dmx512_gpio_config(void);
static void dmx512_uart_config(void);
static void dmx512_timer_config(void);
static void dmx512_dma_config(void);
static void dmx512_send_break(void);
static void dmx512_send_mab(void);
static void dmx512_send_data_byte(uint8_t data);
static void dmx512_send_data_dma(void);
static void dmx512_state_machine(void);

/*!
    \brief      初始化DMX512驱动
    \param[in]  channel_count: 使用的DMX512通道数量 (1-512)
    \param[out] none
    \retval     none
*/
void dmx512_init(uint16_t channel_count)
{
    /* 参数检查 */
    if (channel_count == 0 || channel_count > DMX512_MAX_CHANNELS) {
        channel_count = DMX512_MAX_CHANNELS;
    }
    
    /* 初始化DMX512句柄 */
    memset(&g_dmx512, 0, sizeof(dmx512_handle_t));
    g_dmx512.channel_count = channel_count;
    g_dmx512.state = DMX512_STATE_IDLE;
    g_dmx512.transmission_complete = true;
    g_dmx512.auto_refresh = false;
    g_dmx512.refresh_interval_ms = 25; /* 默认40Hz刷新率 */
    
    /* 设置起始码 */
    g_dmx512.data[0] = DMX512_START_CODE;
    
    /* 配置GPIO */
    dmx512_gpio_config();
    
    /* 配置UART */
    dmx512_uart_config();
    
    /* 配置DMA */
    dmx512_dma_config();
    
    /* 配置定时器 */
    dmx512_timer_config();
    
    /* 禁用数据使能 */
    DMX512_DE_DISABLE();
}

/*!
    \brief      反初始化DMX512驱动
    \param[in]  none
    \param[out] none
    \retval     none
*/
void dmx512_deinit(void)
{
    /* 禁用定时器 */
    timer_disable(TIMER2);
    
    /* 禁用UART */
    usart_disable(DMX512_UART);
    
    /* 禁用数据使能 */
    DMX512_DE_DISABLE();
    
    /* 重置句柄 */
    memset(&g_dmx512, 0, sizeof(dmx512_handle_t));
}

/*!
    \brief      设置DMX512通道数据
    \param[in]  channel: 通道号 (1-512)
    \param[in]  value: 通道值 (0-255)
    \param[out] none
    \retval     none
*/
void dmx512_set_channel(uint16_t channel, uint8_t value)
{
    if (channel >= 1 && channel <= g_dmx512.channel_count) {
        g_dmx512.data[channel] = value; /* 通道1对应数组索引1 */
    }
}

/*!
    \brief      获取DMX512通道数据
    \param[in]  channel: 通道号 (1-512)
    \param[out] none
    \retval     通道值 (0-255)
*/
uint8_t dmx512_get_channel(uint16_t channel)
{
    if (channel >= 1 && channel <= g_dmx512.channel_count) {
        return g_dmx512.data[channel];
    }
    return 0;
}

/*!
    \brief      设置RGB灯带颜色
    \param[in]  start_channel: 起始通道号 (1-510)
    \param[in]  color: RGB颜色值
    \param[out] none
    \retval     none
*/
void dmx512_set_rgb(uint16_t start_channel, rgb_color_t color)
{
    if (start_channel >= 1 && start_channel <= (g_dmx512.channel_count - 2)) {
        g_dmx512.data[start_channel] = color.red; /* GRB顺序：第1个通道为红色 */
        g_dmx512.data[start_channel + 1] = color.blue;            /* GRB顺序：第2个通道为蓝色 */
        g_dmx512.data[start_channel + 2] = color.green; /* GRB顺序：第3个通道为绿色 */
    }
}

/*!
    \brief      设置多个RGB灯带颜色
    \param[in]  start_channel: 起始通道号 (1-510)
    \param[in]  colors: RGB颜色数组
    \param[in]  count: 灯珠数量
    \param[out] none
    \retval     none
*/
void dmx512_set_rgb_array(uint16_t start_channel, rgb_color_t *colors, uint16_t count)
{
    uint16_t i;
    uint16_t channel = start_channel;
    
    for (i = 0; i < count && channel <= (g_dmx512.channel_count - 2); i++) {
        g_dmx512.data[channel] = colors[i].red;        /* GRB顺序：第1个通道为红色 */
        g_dmx512.data[channel + 1] = colors[i].blue;      /* GRB顺序：第2个通道为蓝色 */
        g_dmx512.data[channel + 2] = colors[i].green;     /* GRB顺序：第3个通道为绿色 */
        channel += 3;
    }
}

/*!
    \brief      发送DMX512数据帧
    \param[in]  none
    \param[out] none
    \retval     none
*/
void dmx512_send_frame(void)
{
    if (g_dmx512.transmission_complete) {
        g_dmx512.transmission_complete = false;
        g_dmx512.current_channel = 0;
        g_dmx512.state = DMX512_STATE_BREAK;
        
        /* 启动状态机 */
        dmx512_state_machine();
    }
}

/*!
    \brief      检查DMX512传输是否完成
    \param[in]  none
    \param[out] none
    \retval     true: 传输完成, false: 传输中
*/
bool dmx512_is_transmission_complete(void)
{
    return g_dmx512.transmission_complete;
}

/*!
    \brief      启用DMX512自动刷新
    \param[in]  interval_ms: 刷新间隔(毫秒)
    \param[out] none
    \retval     none
*/
void dmx512_enable_auto_refresh(uint32_t interval_ms)
{
    g_dmx512.auto_refresh = true;
    g_dmx512.refresh_interval_ms = interval_ms;
    
    /* 定时器在timer_config中配置为1us分辨率(PSC=SystemCoreClock/1MHz-1)，
       因此自动重装值应为 interval_ms * 1000 - 1 */
    uint32_t arr = (interval_ms > 0) ? (interval_ms * 1000U) - 1U : 999U; /* 至少1ms */
    timer_autoreload_value_config(TIMER2, arr);
    timer_counter_value_config(TIMER2, 0);
    timer_enable(TIMER2);
}

/*!
    \brief      配置DMX512 DMA
    \param[in]  none
    \param[out] none
    \retval     none
*/
static void dmx512_dma_config(void)
{
    dma_parameter_struct dma_init_struct;
    
    /* 使能DMA时钟 */
    rcu_periph_clock_enable(RCU_DMA);
    rcu_periph_clock_enable(RCU_DMAMUX);
    
    /* 复位DMA通道 */
    dma_deinit(DMX512_DMA_CHANNEL);
    
    /* 配置DMA参数 */
    dma_struct_para_init(&dma_init_struct);
    dma_init_struct.request = DMX512_DMA_REQUEST;
    dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL;
    dma_init_struct.memory_addr = (uint32_t)g_dmx512.data;
    dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
    dma_init_struct.number = g_dmx512.channel_count + 1;  /* 包含起始码 */
    dma_init_struct.periph_addr = (uint32_t)&USART_TDATA(DMX512_UART);
    dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
    dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
    dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
    dma_init(DMX512_DMA_CHANNEL, &dma_init_struct);
    
    /* 配置DMA模式 */
    dma_circulation_disable(DMX512_DMA_CHANNEL);
    dma_memory_to_memory_disable(DMX512_DMA_CHANNEL);
    
    /* 禁用DMAMUX同步模式 */
    dmamux_synchronization_disable(DMAMUX_MUXCH1);
    
    /* 配置DMA中断 */
    nvic_irq_enable(DMX512_DMA_IRQ, 3);  /* 优先级 */
    dma_interrupt_enable(DMX512_DMA_CHANNEL, DMA_INT_FTF);  /* 传输完成中断 */
    
    /* 使能UART的DMA发送功能 */
    usart_dma_transmit_config(DMX512_UART, USART_TRANSMIT_DMA_ENABLE);
}

/*!
    \brief      禁用DMX512自动刷新
    \param[in]  none
    \param[out] none
    \retval     none
*/
void dmx512_disable_auto_refresh(void)
{
    g_dmx512.auto_refresh = false;
    timer_disable(TIMER2);
}

/*!
    \brief      配置DMX512 GPIO
    \param[in]  none
    \param[out] none
    \retval     none
*/
static void dmx512_gpio_config(void)
{
    /* 使能GPIO时钟 */
    rcu_periph_clock_enable(RCU_GPIOA);
    
    /* 配置UART TX引脚 PA2 */
    gpio_af_set(DMX512_TX_PORT, DMX512_TX_AF, DMX512_TX_PIN);
    gpio_mode_set(DMX512_TX_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, DMX512_TX_PIN);
    gpio_output_options_set(DMX512_TX_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_LEVEL_1, DMX512_TX_PIN);
    
    /* 配置数据使能引脚 PA1（可选）*/
#ifdef DMX512_USE_DE
    gpio_mode_set(DMX512_DE_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, DMX512_DE_PIN);
    gpio_output_options_set(DMX512_DE_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_LEVEL_1, DMX512_DE_PIN);
#endif
    
    /* 初始状态：禁用数据使能 */
    DMX512_DE_DISABLE();
}

/*!
    \brief      配置DMX512 UART
    \param[in]  none
    \param[out] none
    \retval     none
*/
static void dmx512_uart_config(void)
{
    /* 使能UART时钟 */
    rcu_periph_clock_enable(RCU_USART1);
    
    /* 复位UART */
    usart_deinit(DMX512_UART);
    
    /* 配置UART参数 */
    usart_baudrate_set(DMX512_UART, DMX512_UART_BAUDRATE);
    usart_word_length_set(DMX512_UART, USART_WL_8BIT);
    usart_stop_bit_set(DMX512_UART, USART_STB_2BIT); /* DMX512使用2个停止位 */
    usart_parity_config(DMX512_UART, USART_PM_NONE);
    usart_hardware_flow_rts_config(DMX512_UART, USART_RTS_DISABLE);
    usart_hardware_flow_cts_config(DMX512_UART, USART_CTS_DISABLE);
    usart_receive_config(DMX512_UART, USART_RECEIVE_ENABLE);   /* 启用接收，支持DMX512接收器 */
    usart_transmit_config(DMX512_UART, USART_TRANSMIT_ENABLE);
    
    /* 使能UART */
    usart_enable(DMX512_UART);
    
    /* 配置UART中断 */
    nvic_irq_enable(DMX512_UART_IRQ, 3);
    usart_interrupt_enable(DMX512_UART, USART_INT_TC); /* 传输完成中断 */
}

/*!
    \brief      配置DMX512定时器
    \param[in]  none
    \param[out] none
    \retval     none
*/
static void dmx512_timer_config(void)
{
    timer_parameter_struct timer_initpara;
    
    /* 使能定时器时钟 */
    rcu_periph_clock_enable(RCU_TIMER2);
    
    /* 复位定时器 */
    timer_deinit(TIMER2);
    
    /* 配置定时器参数 */
    timer_initpara.prescaler = SystemCoreClock / 1000000 - 1; /* 1us分辨率 */
    timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
    timer_initpara.counterdirection = TIMER_COUNTER_UP;
    timer_initpara.period = 1000 - 1; /* 1ms周期，后续会根据需要调整 */
    timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
    timer_init(TIMER2, &timer_initpara);
    
    /* 配置定时器中断 */
    nvic_irq_enable(TIMER2_IRQn, 1);
    timer_interrupt_enable(TIMER2, TIMER_INT_UP);
}

/*!
    \brief      发送Break信号
    \param[in]  none
    \param[out] none
    \retval     none
*/
static void dmx512_send_break(void)
{
    /* 使能数据使能 */
    DMX512_DE_ENABLE();
    
    /* 手动发送Break信号：将TX引脚拉低 */
    /* 禁用UART发送功能 */
    usart_transmit_config(DMX512_UART, USART_TRANSMIT_DISABLE);
    
    /* 将TX引脚配置为普通输出模式并拉低 */
    gpio_mode_set(DMX512_TX_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, DMX512_TX_PIN);
    gpio_bit_reset(DMX512_TX_PORT, DMX512_TX_PIN);
    
    /* 设置定时器为Break时间 */
    timer_autoreload_value_config(TIMER2, DMX512_BREAK_TIME_US - 1);
    timer_counter_value_config(TIMER2, 0);
    timer_enable(TIMER2);
}

/*!
    \brief      发送Mark After Break信号
    \param[in]  none
    \param[out] none
    \retval     none
*/
static void dmx512_send_mab(void)
{
    /* 恢复TX引脚为UART复用功能 */
    gpio_af_set(DMX512_TX_PORT, DMX512_TX_AF, DMX512_TX_PIN);
    gpio_mode_set(DMX512_TX_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, DMX512_TX_PIN);
    gpio_output_options_set(DMX512_TX_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_LEVEL_1, DMX512_TX_PIN);
    
    /* 重新使能UART发送功能 */
    usart_transmit_config(DMX512_UART, USART_TRANSMIT_ENABLE);
    
    /* 设置定时器为MAB时间 */
    timer_autoreload_value_config(TIMER2, DMX512_MAB_TIME_US - 1);
    timer_counter_value_config(TIMER2, 0);
    timer_enable(TIMER2);
}

/*!
    \brief      发送数据字节
    \param[in]  data: 要发送的数据
    \param[out] none
    \retval     none
*/
static void dmx512_send_data_byte(uint8_t data)
{
    /* 发送数据 */
    usart_data_transmit(DMX512_UART, data);
}

/*!
    \brief      使用DMA发送DMX512数据
    \param[in]  none
    \param[out] none
    \retval     none
*/
static void dmx512_send_data_dma(void)
{
    /* 禁用DMA通道 */
    dma_channel_disable(DMX512_DMA_CHANNEL);
    
    /* 更新DMA传输数量 */
    dma_transfer_number_config(DMX512_DMA_CHANNEL, g_dmx512.channel_count + 1);
    
    /* 清除DMA中断标志 */
    dma_interrupt_flag_clear(DMX512_DMA_CHANNEL, DMA_INT_FLAG_FTF);
    
    /* 启动DMA传输 */
    dma_channel_enable(DMX512_DMA_CHANNEL);
}

/*!
    \brief      DMX512状态机
    \param[in]  none
    \param[out] none
    \retval     none
*/
static void dmx512_state_machine(void)
{
    switch (g_dmx512.state) {
        case DMX512_STATE_IDLE:
            /* 空闲状态，不做任何操作 */
            break;
            
        case DMX512_STATE_BREAK:
            /* 发送Break信号 */
            dmx512_send_break();
            break;
            
        case DMX512_STATE_MAB:
            /* 发送Mark After Break */
            dmx512_send_mab();
            break;
            
        case DMX512_STATE_DATA:
            /* 使用DMA发送所有数据 */
            dmx512_send_data_dma();
            /* 状态切换到完成，等待DMA中断 */
            g_dmx512.state = DMX512_STATE_COMPLETE;
            break;
            
        case DMX512_STATE_COMPLETE:
            /* 传输完成 */
            g_dmx512.state = DMX512_STATE_IDLE;
            break;
            
        default:
            g_dmx512.state = DMX512_STATE_IDLE;
            break;
    }
}

/*!
    \brief      DMX512定时器中断处理函数
    \param[in]  none
    \param[out] none
    \retval     none
*/
void dmx512_timer_irq_handler(void)
{
    if (SET == timer_interrupt_flag_get(TIMER2, TIMER_INT_FLAG_UP)) {
        timer_interrupt_flag_clear(TIMER2, TIMER_INT_FLAG_UP);
        timer_disable(TIMER2);
        
        switch (g_dmx512.state) {
            case DMX512_STATE_BREAK:
                /* Break信号完成，转到MAB状态 */
                g_dmx512.state = DMX512_STATE_MAB;
                dmx512_state_machine();
                break;
                
            case DMX512_STATE_MAB:
                /* MAB完成，开始发送数据 */
                g_dmx512.state = DMX512_STATE_DATA;
                g_dmx512.current_channel = 0; /* 从起始码开始 */
                dmx512_state_machine();
                break;
                
            default:
                /* 自动刷新定时器 */
                if (g_dmx512.auto_refresh && g_dmx512.transmission_complete) {
                    dmx512_send_frame();
                }
                break;
        }
    }
}

/*!
    \brief      DMX512 UART中断处理函数
    \param[in]  none
    \param[out] none
    \retval     none
*/
void dmx512_uart_irq_handler(void)
{
    if (SET == usart_interrupt_flag_get(DMX512_UART, USART_INT_FLAG_TC)) {
        usart_interrupt_flag_clear(DMX512_UART, USART_INT_FLAG_TC);
        
        if (g_dmx512.state == DMX512_STATE_DATA) {
            g_dmx512.current_channel++;
            dmx512_state_machine();
        }
    }
}

/*!
    \brief      DMX512 DMA中断处理函数
    \param[in]  none
    \param[out] none
    \retval     none
*/
void dmx512_dma_irq_handler(void)
{
    if(dma_interrupt_flag_get(DMX512_DMA_CHANNEL, DMA_INT_FLAG_FTF)) {
        /* 清除DMA传输完成中断标志 */
        dma_interrupt_flag_clear(DMX512_DMA_CHANNEL, DMA_INT_FLAG_FTF);
        
        /* 禁用DMA通道 */
        dma_channel_disable(DMX512_DMA_CHANNEL);
        
        /* 标记传输完成 */
        g_dmx512.transmission_complete = true;
        g_dmx512.state = DMX512_STATE_IDLE;
        
        /* 禁用数据使能 */
        DMX512_DE_DISABLE();
        
        /* 若启用自动刷新，则在完成一帧后按刷新间隔重新启动定时器 */
        if (g_dmx512.auto_refresh) {
            uint32_t arr = (g_dmx512.refresh_interval_ms > 0U) ? (g_dmx512.refresh_interval_ms * 1000U) - 1U : 999U;
            timer_autoreload_value_config(TIMER2, arr);
            timer_counter_value_config(TIMER2, 0);
            timer_enable(TIMER2);
        }
    }
}

/*!
    \brief      设置所有灯带为单一颜色
    \param[in]  color: RGB颜色值
    \param[in]  led_count: 灯珠数量
    \param[out] none
    \retval     none
*/
void dmx512_set_all_color(rgb_color_t color, uint16_t led_count)
{
    uint16_t i;
    uint16_t channel = 1;
    
    for (i = 0; i < led_count && channel <= (g_dmx512.channel_count - 2); i++) {
        dmx512_set_rgb(channel, color);
        channel += 3;
    }
}

/*!
    \brief      彩虹效果
    \param[in]  led_count: 灯珠数量
    \param[in]  offset: 颜色偏移量
    \param[out] none
    \retval     none
*/
void dmx512_rainbow_effect(uint16_t led_count, uint8_t offset)
{
    uint16_t i;
    uint16_t channel = 1;
    uint16_t hue;
    rgb_color_t color;
    
    for (i = 0; i < led_count && channel <= (g_dmx512.channel_count - 2); i++) {
        hue = (i * 360 / led_count + offset) % 360;
        color = dmx512_hsv_to_rgb(hue, 255, 255);
        dmx512_set_rgb(channel, color);
        channel += 3;
    }
}

/*!
    \brief      呼吸灯效果
    \param[in]  color: 基础颜色
    \param[in]  led_count: 灯珠数量
    \param[in]  brightness: 亮度 (0-255)
    \param[out] none
    \retval     none
*/
void dmx512_breathing_effect(rgb_color_t color, uint16_t led_count, uint8_t brightness)
{
    rgb_color_t adjusted_color = dmx512_adjust_brightness(color, brightness);
    dmx512_set_all_color(adjusted_color, led_count);
}

/*!
    \brief      流水灯效果
    \param[in]  color: 流水灯颜色
    \param[in]  led_count: 灯珠数量
    \param[in]  position: 当前位置
    \param[in]  tail_length: 拖尾长度
    \param[out] none
    \retval     none
*/
void dmx512_flowing_effect(rgb_color_t color, uint16_t led_count, uint16_t position, uint8_t tail_length)
{
    uint16_t i;
    uint16_t channel = 1;
    rgb_color_t led_color;
    uint8_t brightness;
    
    /* 清除所有LED */
    dmx512_set_all_color(DMX512_COLOR_BLACK, led_count);
    
    /* 设置流水灯 */
    for (i = 0; i < tail_length && i <= position; i++) {
        if ((position - i) < led_count) {
            brightness = 255 * (tail_length - i) / tail_length;
            led_color = dmx512_adjust_brightness(color, brightness);
            channel = 1 + (position - i) * 3;
            if (channel <= (g_dmx512.channel_count - 2)) {
                dmx512_set_rgb(channel, led_color);
            }
        }
    }
}

/*!
    \brief      HSV转RGB颜色
    \param[in]  hue: 色调 (0-359)
    \param[in]  saturation: 饱和度 (0-255)
    \param[in]  value: 明度 (0-255)
    \param[out] none
    \retval     RGB颜色值
*/
rgb_color_t dmx512_hsv_to_rgb(uint16_t hue, uint8_t saturation, uint8_t value)
{
    rgb_color_t rgb = {0, 0, 0};
    uint8_t region, remainder, p, q, t;
    
    if (saturation == 0) {
        rgb.red = value;
        rgb.green = value;
        rgb.blue = value;
        return rgb;
    }
    
    region = hue / 60;
    remainder = (hue - (region * 60)) * 255 / 60;
    
    p = (value * (255 - saturation)) >> 8;
    q = (value * (255 - ((saturation * remainder) >> 8))) >> 8;
    t = (value * (255 - ((saturation * (255 - remainder)) >> 8))) >> 8;
    
    switch (region) {
        case 0:
            rgb.red = value; rgb.green = t; rgb.blue = p;
            break;
        case 1:
            rgb.red = q; rgb.green = value; rgb.blue = p;
            break;
        case 2:
            rgb.red = p; rgb.green = value; rgb.blue = t;
            break;
        case 3:
            rgb.red = p; rgb.green = q; rgb.blue = value;
            break;
        case 4:
            rgb.red = t; rgb.green = p; rgb.blue = value;
            break;
        default:
            rgb.red = value; rgb.green = p; rgb.blue = q;
            break;
    }
    
    return rgb;
}

/*!
    \brief      调整RGB颜色亮度
    \param[in]  color: 原始颜色
    \param[in]  brightness: 亮度系数 (0-255)
    \param[out] none
    \retval     调整后的RGB颜色值
*/
rgb_color_t dmx512_adjust_brightness(rgb_color_t color, uint8_t brightness)
{
    rgb_color_t result;
    
    result.red = (color.red * brightness) >> 8;
    result.green = (color.green * brightness) >> 8;
    result.blue = (color.blue * brightness) >> 8;
    
    return result;
}